package com.cskaoyan.javase.string._4api;

/**
 * @description: String类的比较功能
 * @author: wuguidong@cskaoyan.onaliyun.com
 **/

import java.util.Arrays;
import java.util.Comparator;

/**
 * String类的比较功能其实是有两类的:
 * 第一类:
 *      boolean equals(String str) 比较字符串是否相等,区分大小写
 *      boolean equalsIgnoreCase(String str) 比较字符串是否相等,不区分大小写
 *          equals注重比较字符串是否相等,比较字符串内容是否一样,它只有相等的概念,没有大小的概念
 *
 * 第二类:
 *      int compareTo(String str) 比较两个字符串的大小,区分大小写
 *      int compareToIgnoreCase(String str) 比较两个字符串的大小,不区分大小写
 *          compareTo专注于比较字符串的大小,它有大小的概念,这个大小是是为了排序而存在的
 *          在排序中,小的字符串在前面,大的字符串在后面
 *
 * String类当中compareTo方法实现:
 *      通过逐位比较两个字符串的内容,如果出现第一个字符不相同的情况,就返回它们的编码值之差(调用者-参数)
 *      例如:"abc".compareTo("bc")返回-1
 *           "bc".compareTo("abc")返回1
 *      如果比较的过程中,它们的字符都是一样的,没有出现字符不同的情况,返回两个value数组长度之差(调用者-参数)
 *      例如: "abc".compareTo("ab")返回1
 *            "ab".compareTo("abc")返回-1
 *       最特殊的情况下,两个字符串长度一样,内容也一样,该方法返回0
 *       例如: "abc".compareTo("abc")返回0
 * 综上,该方法实际上是返回两个字符串第一位不相同字符的编码值之差,如果字符在比较中都相同,就返回长度之差
 *
 * 类比于Double.compare(d1,d2)可以看成d1-d2,方法返回负数意味着d1 < d2,正数意味着d1>d2,0意味着相等
 * str1.compareTo(str2)可以看成是str1-str2,方法返回负数意味着str1 < str2,正数意味着str1>str2,0意味着相等
 *
 * 实际上在Java当中,所有实现Comparable接口的类都可以对它的对象进行自然排序,按照compareTo方法指示的大小进行排序
 *      compareTo方法就是它们的自然排序方法
 *
 * 实现自然排序接口和方法的类的集合或者数组,可以通过数组工具类和集合工具类中的sort方法,完成排序
 *      Arrays.sort(Object[] arr)
 *      Collections.sort(Collection c)
 *      这些方法都是没有返回值,而且直接在原先数组上完成排序,不会创建新数组(集合)
 *
 *      String的自然排序是根据字符的编码值排序的,编码值越大就越在后面,这很符合"字典"的特点,所以String的自然排序也叫字典排序
 *
 * 接下来给自定义类实现自然排序,只需要该类实现Comparable接口即可.
 * 关于自然排序需要补充几个细节:
 *      Array.sort方法会自动两两使用compareTo进行比较,不需要关心它的实现,就会按照集合/数组中从小到大排序
 *      compareTo方法可以看成是方法的调用者对象-方法的参数对象,这样就容易根据条件写出对应的比较规则了
 *
 * 基于Comparable接口实现的自然排序必须要求修改某个类的源代码,这在很多时候是做不到的.如果仅仅是为了完成一个排序,要修改源代码还是还是有些麻烦的.并且使用它实现自然排序也不能用lambda表达式
 *  最后还有一个非常重要的问题:
 *      类中的compareTo方法和equals方法都是判断对象大小的,它们两个应该保持一致性,这样逻辑上才通畅的
 *      如果equals方法认为相等,compareTo方法也应该返回0
 *      如果equals方法认为不相等,compareTo方法就不应该返回0,应该返回非0
 *      所以这就要求equals方法和compareTo重写依据的内容是一致的,这样才能保持一致性
 *
 * 综上,使用实现Comparable接口的方式完成自然排序,限制很多,不是很方便,所以引入了基于Comparator接口实现的自然排序
 * 首先在进行排序时,需要一个特殊的方法:
 *      带比较器的Arrays.sort方法,其中Comparator接口的实现类对象就是比较器,该对象通过compare方法传入比较的规则
 *
 *      表示传入比较规则的int compare(T o1, T o2)方法:
 *          该方法可以看成是o1-o2,如果方法返回负数,意外这o1< o2,相反则大于,只有当方法返回0时,才表示对象相等
 *
 *
 * 使用lambda表达式传入比较器对象实现自然排序,更加方便,更加简洁,更加的优雅,它是最常用的方式
 *
 * 自然排序的意义:
 *      在正常开发中给数据排序是非常常见的操作,一般情况下,在从数据库中查找数据后可以顺便就完成排序(使用SQL语句操作数据库)
 *      但是在有些情况下,不方便使用数据库,更方便用集合/数组进行存储数据时,这时对数据的排序就必须依赖自然排序了
 *      而且NoSQL的数据库(比如redis)没有SQL语句操作数据,就必须依赖对象的自然排序完成排序~
 *
 *
 */
public class Demo2 {
    public static void main(String[] args) {
        //手动排序,按照compareTo方法的规则:
        //a,aaa,abc,bbb,bcd,ccc,yyy
        String[] arr = {"abc", "bcd", "aaa", "bbb", "ccc", "a", "yyy"};
        Arrays.sort(arr);
        System.out.println(Arrays.toString(arr));

        /*Student s1 = new Student(18, "张三", 90);
        Student s2 = new Student(11, "李四", 77);
        Student s3 = new Student(12, "王二", 89);
        Student s4 = new Student(6, "麻子", 46);
        Student s5 = new Student(8, "老八", 69);
        Student s6 = new Student(9, "起名字好不容易", 90);
        Student s7 = new Student(88, "我的名字比你长,所以我更厉害", 32);
        Student s8 = new Student(99, "女孩为何穿短裙", 66);
        Student s9 = new Student(100, "小仙若", 98);
        Student s10 = new Student(16, "长风", 100);*/


        Student s1 = new Student(18, "我的名字比你长,所以我更厉害", 100);
        Student s2 = new Student(18, "李四", 100);
        Student s3 = new Student(18, "李四的儿子", 100);
        Student s4 = new Student(18, "李四老婆", 100);
        Student s5 = new Student(18, "老八的汉堡包", 100);
        Student s6 = new Student(9, "起名字好不容易", 100);
        Student s7 = new Student(88, "张三", 100);
        Student s8 = new Student(99, "女孩为何穿短裙", 100);
        Student s9 = new Student(100, "小仙若", 100);
        Student s10 = new Student(16, "长风", 120);

        Student[] arr2 = {s1, s2, s3, s4, s5, s6, s7, s8, s9, s10};
        Arrays.sort(arr2);
        System.out.println(Arrays.toString(arr2));

        System.out.println("-------------------------------------------");
        //朴素的实现方法]
        Arrays.sort(arr2, new MyCompare());
        System.out.println(Arrays.toString(arr2));

        //高级一点的实现方法
        Arrays.sort(arr2, new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                //比较的规则: 按照年龄从大到小
                return o2.getAge() - o1.getAge();
            }
        });
        System.out.println(Arrays.toString(arr2));

        //使用高贵优雅的lambda表达式
        //比较的规则: 分数越低越靠前
        Arrays.sort(arr2, (stu1, stu2) -> ((int) (stu1.getScore() - stu2.getScore())));
        System.out.println(Arrays.toString(arr2));

        //使用lambda表达式方法引用
        Arrays.sort(arr2, Demo2::oursCompare);
        System.out.println(Arrays.toString(arr2));
    }

    /**
     *   比较的规则:
     *   首先按照分数排序,分数越高越在前面
     *   如果分数一样,按照年龄排序,年龄越小越在前面
     *   如果年龄也一样了,那么名字越长越在前面
     */
    //使用方法引用,单独给出compare方法的实现
    public static int oursCompare(Student s1, Student s2) {
        //按照分数排序
        if (s1.getScore() != s2.getScore()) return ((int) (s2.getScore() - s1.getScore()));
        //意味着分数相同,按照年龄排序
        if (s1.getAge() != s2.getAge()) return s1.getAge() - s2.getAge();
        //意味着分数和年龄都相同,比较名字的长度
        return s2.getName().length() - s1.getName().length();
    }


}

class MyCompare implements Comparator<Student> {
    @Override
    public int compare(Student o1, Student o2) {
        //该方法看成是o1-o2
        //比较的规则: 姓名按照长度从短到长排序
        return o1.getName().length() - o2.getName().length();
    }
}

class Student implements Comparable<Student> {
    private int age;
    private String name;
    private double score;

    public int getAge() {
        return age;
    }

    public String getName() {
        return name;
    }

    public double getScore() {
        return score;
    }

    public Student() {
    }

    public Student(int age, String name, double score) {
        this.age = age;
        this.name = name;
        this.score = score;
    }

    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +
                ", name='" + name + '\'' +
                ", score=" + score +
                '}';
    }

    //自然排序方法,是为了给自然排序提供规则的
    //规则1: 按照年龄从小到大排列,意味着年龄越小,对象越小,该方法就越应该返回负数
    //规则2: 按照分数从高到低排序,意味着分数越高,对象越小,该方法就越应该返回负数
    @Override
    public int compareTo(Student s) {
        //return  s.age -this.age;
        //return ((int) (s.score - this.score));
        //Double.compare(d1,d2)
        return Double.compare(s.score, this.score);
    }
}